/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package net.siit.infovis.prefuse.render;

import java.awt.BasicStroke;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;

import java.awt.geom.Rectangle2D;
import prefuse.Constants;
import prefuse.render.EdgeRenderer;
import prefuse.util.ColorLib;
import prefuse.util.GraphicsLib;
import prefuse.visual.EdgeItem;
import prefuse.visual.VisualItem;

/**
 *
 * @author arthit
 */
public class LabeledEdgeRenderer extends EdgeRenderer {
    // label handling
    protected String  m_textLabelName = null; // label string
    protected String  m_sizeLabelName = null; // label thickness
    protected Shape   m_curLabel  = null; // label shape
    
    public LabeledEdgeRenderer(String textLabelName, String sizeLabelName, int edgeType, int arrowType) {
        super(edgeType, arrowType);
        m_textLabelName = textLabelName;
        m_sizeLabelName = sizeLabelName;
    }
    
    /**
     * Get the field name to use for text labels.
     * @return the data field for text labels, or null for no text
     */
    public String getTextField() {
        return m_textLabelName;
    }
    
    /**
     * Set the field name to use for text labels.
     * @param textField the data field for text labels, or null for no text
     */
    public void setTextField(String textField) {
        m_textLabelName = textField;
    }

    /**
     * Returns the text to draw. Subclasses can override this class to
     * perform custom text selection.
     * @param item the item to represent as a <code>String</code>
     * @return a <code>String</code> to draw
     */
    protected String getTextData(VisualItem item) {
        String s = null;
        if ( item.canGetString(m_textLabelName) ) {
            return item.getString(m_textLabelName);            
        }
        return s;
    }

    /**
     * Get the field name to use for node size.
     * @return the data field for node size
     */
    public String getSizeField() {
        return m_sizeLabelName;
    }
    
    /**
     * Set the field name to use for node size.
     * @param sizeField the data field for node size
     */
    public void setSizeField(String sizeField) {
        m_sizeLabelName = sizeField;
    }

    /**
     * Returns the node size. Subclasses can override this class to
     * perform custom size selection.
     * @param item the item to get size
     * @return a size to draw
     */
    protected double getSizeData(VisualItem item) {
        if ( item.canGetString(m_sizeLabelName) ) {
            try {
                return (Double.parseDouble(item.getString(m_sizeLabelName)) );
            } catch (NumberFormatException e) {
                return 0; // size data can't be parse (wrong number format)
            }
        }
        return 0; // no size data available
    }

    /**
     * @see prefuse.render.AbstractShapeRenderer#getRawShape(prefuse.visual.VisualItem)
     */
    @Override
    protected Shape getRawShape(VisualItem item) {
        EdgeItem   edge = (EdgeItem)item;
        VisualItem item1 = edge.getSourceItem();
        VisualItem item2 = edge.getTargetItem();
        
        int type = m_edgeType;
        
        getAlignedPoint(m_tmpPoints[0], item1.getBounds(),
                        m_xAlign1, m_yAlign1);
        getAlignedPoint(m_tmpPoints[1], item2.getBounds(),
                        m_xAlign2, m_yAlign2);
        m_curWidth = (float)(m_width * getLineWidth(item) * getSizeData(item));
        
        // create the arrow head, if needed
        EdgeItem e = (EdgeItem)item;
        if ( e.isDirected() && m_edgeArrow != Constants.EDGE_ARROW_NONE ) {
            // get starting and ending edge endpoints
            boolean forward = (m_edgeArrow == Constants.EDGE_ARROW_FORWARD);
            Point2D start = null, end = null;
            start = m_tmpPoints[forward?0:1];
            end   = m_tmpPoints[forward?1:0];
            
            // compute the intersection with the target bounding box
            VisualItem dest = forward ? e.getTargetItem() : e.getSourceItem();
            int i = GraphicsLib.intersectLineRectangle(start, end,
                    dest.getBounds(), m_isctPoints);
            if ( i > 0 ) end = m_isctPoints[0];
            
            // create the arrow head shape
            AffineTransform at = getArrowTrans(start, end, m_curWidth);
            m_curArrow = at.createTransformedShape(m_arrowHead);
            
            // update the endpoints for the edge shape
            // need to bias this by arrow head size
            Point2D lineEnd = m_tmpPoints[forward?1:0]; 
            lineEnd.setLocation(0, -m_arrowHeight);
            at.transform(lineEnd, lineEnd);
        } else {
            m_curArrow = null;
        }
        
        // create the edge shape
        Shape shape = null;
        double n1x = m_tmpPoints[0].getX();
        double n1y = m_tmpPoints[0].getY();
        double n2x = m_tmpPoints[1].getX();
        double n2y = m_tmpPoints[1].getY();
        switch ( type ) {
            case Constants.EDGE_TYPE_LINE: 
                /*
                double dX = n2x - n1x;
                double dY = n2y - n1y;
                // line length
                double lineLength = Math.sqrt(dX * dX + dY * dY);
                // line width
                double lineWidth = getSizeData(item)*10.0;
                
                // thickness
                double angle = Math.atan(dY/dX);
                double a = Math.cos(angle) * (lineWidth/2.0);
                double b = Math.sin(angle) * (lineWidth/2.0);

                shape = new Path2D.Double();
                if ((dX >= 0) && (dY >= 0)) {
                    ((Path2D)shape).moveTo(n1x+a, n1y-b);
                    ((Path2D)shape).lineTo(n1x-a, n1y+b);
                    ((Path2D)shape).lineTo(n2x-a, n2y+b);
                    ((Path2D)shape).lineTo(n2x+a, n2y-b);
                } else
                if ((dX <= 0) && (dY <= 0)) {
                    ((Path2D)shape).moveTo(n1x-a, n1y+b);
                    ((Path2D)shape).lineTo(n1x+a, n1y-b);
                    ((Path2D)shape).lineTo(n2x+a, n2y-b);
                    ((Path2D)shape).lineTo(n2x-a, n2y+b);
                } else
                if ((dX >= 0) && (dY <= 0)) {
                    ((Path2D)shape).moveTo(n1x-a, n1y+b);
                    ((Path2D)shape).lineTo(n1x+a, n1y-b);
                    ((Path2D)shape).lineTo(n2x+a, n2y-b);
                    ((Path2D)shape).lineTo(n2x-a, n2y+b);                    
                } else
                if ((dX <= 0) && (dY >= 0)) {
                    ((Path2D)shape).moveTo(n1x+a, n1y-b);
                    ((Path2D)shape).lineTo(n1x-a, n1y+b);
                    ((Path2D)shape).lineTo(n2x-a, n2y+b);
                    ((Path2D)shape).lineTo(n2x+a, n2y-b);                    
                }

                ((Path2D)shape).closePath();
                */
                m_line.setLine(n1x, n1y, n2x, n2y);                                
                shape = m_line;
                break;
            case Constants.EDGE_TYPE_CURVE:
                getCurveControlPoints(edge, m_ctrlPoints,n1x,n1y,n2x,n2y);
                m_cubic.setCurve(n1x, n1y,
                                m_ctrlPoints[0].getX(), m_ctrlPoints[0].getY(),
                                m_ctrlPoints[1].getX(), m_ctrlPoints[1].getY(),
                                n2x, n2y);
                shape = m_cubic;
                break;
            default:
                throw new IllegalStateException("Unknown edge type");
        }
        
        if (m_textLabelName != null) {
            // draw label here
        }
        
        // return the edge shape
        return shape;
    }

    /**
     * @see prefuse.render.Renderer#render(java.awt.Graphics2D, prefuse.visual.VisualItem)
     */
    public void render(Graphics2D g, VisualItem item) {
        // render the edge line
        super.render(g, item);
        // render the edge label, if appropriate
        if ( m_curLabel != null ) {
            g.setPaint(ColorLib.getColor(item.getFillColor()));
            g.fill(m_curLabel);
        }
    }

}
